/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.velocityme.session;

import com.velocityme.entity.Bookmark;
import com.velocityme.entity.ChangeDelta;
import com.velocityme.entity.Group;
import com.velocityme.entity.Node;
import com.velocityme.entity.User;
import com.velocityme.enums.ChangeDeltaItemType;
import com.velocityme.enums.ChangeDeltaType;
import com.velocityme.enums.Permission;
import com.velocityme.utility.DuplicateUserException;
import com.velocityme.utility.PasswordEncrypter;
import com.velocityme.utility.PasswordIncorrectException;
import com.velocityme.utility.PermissionDeniedException;
import java.util.Collection;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Stateless;
import javax.naming.ServiceUnavailableException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

/**
 *
 * @author rcrida
 */
@Stateless
public class UserBean implements UserLocal {
    @EJB
    private NodeLocal nodeBean;

    @PersistenceContext
    private EntityManager em;

    @EJB
    private SearchLocal m_searchLocal;

    /**
     * Check  to see if the specified username already exists in the database
     *
     * @param p_username The username to check the database for
     * @return true if the username already exists
     **/
    private boolean isNameAlreadyUsed(String name) {
        try {
            em.createNamedQuery("findUserByName").setParameter("name", name).getSingleResult();
        } catch (NoResultException e) {
            return false;
        }
        return true;
    }

    public User createUser(KeyLocal key, User user, String password) {
        try {
            key.assertHasPermission(user.getParentNode(), Permission.USER_CREATE);
            if (isNameAlreadyUsed(user.getName()))
                throw new DuplicateUserException();
            user.setEnabled(false);
            user.setPasswordHash(PasswordEncrypter.getInstance().encrypt(password));
            ChangeDelta changeDelta = user.addChangeDelta(ChangeDeltaType.CREATE, key.getUser());
            nodeBean.logCreate(user, changeDelta);
            changeDelta.addItem(ChangeDeltaItemType.PERSON, user.getPerson().getName());
            em.persist(user);
            em.persist(changeDelta);
            em.merge(user.getPerson());
            return user;
        } catch (ServiceUnavailableException e) {
            throw new EJBException(e);
        }
    }

    public void editUser(KeyLocal key, User user) {
        User current = em.find(User.class, user.getId());
        key.assertHasPermission(user, Permission.USER_EDIT);
        if (!user.getName().equals(current.getName()) && isNameAlreadyUsed(user.getName()))
            throw new DuplicateUserException();
        user.setEnabled(current.isEnabled());
        ChangeDelta changeDelta = user.addChangeDelta(ChangeDeltaType.EDIT, key.getUser());
        nodeBean.logEdit(user, current, changeDelta);
        if (!user.getPerson().getId().equals(current.getPerson().getId())) {
            changeDelta.addItem(ChangeDeltaItemType.PERSON, user.getPerson().getName());
            current.getPerson().getUsers().remove(user);
            em.merge(current.getPerson());
        }
        em.persist(changeDelta);
        em.merge(user);
        em.merge(user.getPerson());
    }

    @Override
    public User findUser(KeyLocal key, Long userId) {
        User user = em.find(User.class, userId);
        key.assertHasPermission(user, Permission.NODE_VIEW);
        return user;
    }

    public void changePassword(KeyLocal key, Long userId, String oldPassword, String newPassword) {
        try {
            User user = em.find(User.class, userId);
            String newEncrypted = PasswordEncrypter.getInstance().encrypt(newPassword);
            if (m_searchLocal.getAssignedPermissions(key, user).contains(Permission.USER_CHANGE_PASSWORD)) {
                user.setPasswordHash(newEncrypted);
                ChangeDelta changeDelta = user.addChangeDelta(ChangeDeltaType.SET_PASSWORD, user);
                em.merge(user);
                em.persist(changeDelta);
            }
            else
                if (user.equals(key.getUser())) {
                    String oldEncrypted = PasswordEncrypter.getInstance().encrypt(oldPassword);
                    if (oldEncrypted.equals(user.getPasswordHash())) {
                        user.setPasswordHash(newEncrypted);
                        ChangeDelta changeDelta = user.addChangeDelta(ChangeDeltaType.SET_PASSWORD, user);
                        em.merge(user);
                        em.persist(changeDelta);
                    } else {
                        throw new PasswordIncorrectException();
                    }
                } else {
                    throw new PermissionDeniedException();
                }
        } catch (ServiceUnavailableException e) {
            throw new EJBException(e);
        }
    }

    public void setUserEnabled(KeyLocal key, Long userId, boolean enabled) {
        User user = em.find(User.class, userId);
        key.assertHasPermission(user, Permission.USER_ENABLE);
        if (user.isEnabled() != enabled) {
            ChangeDelta changeDelta = user.addChangeDelta(ChangeDeltaType.EDIT, user);
            changeDelta.addItem(ChangeDeltaItemType.ENABLED, Boolean.toString(enabled));
            user.setEnabled(enabled);
        }
    }

    public void addBookmarkNode(KeyLocal key, Long nodeId) {
        key.validateKey();
        Node node = em.find(Node.class, nodeId);
        User user = key.getUser();
        em.persist(new Bookmark(user, node));
    }

    public void removeBookmarkNode(KeyLocal key, Long nodeId) {
        key.validateKey();
        Node node = em.find(Node.class, nodeId);
        User user = key.getUser();
        try {
            em.remove(em.createNamedQuery("findBookmark").setParameter("user", user).setParameter("node", node).getSingleResult());
        } catch(NoResultException ex) {
            // ignore
        }
    }

    public void showBookmarks(KeyLocal key, boolean show) {
        key.validateKey();
        User user = key.getUser();
        user.setShowBookmarks(show);
        em.merge(user);
    }

    @Override
    public Collection<User> findAllUsers(KeyLocal key) {
        key.validateKey();
        return em.createNamedQuery("findAllUsers").getResultList();
    }

    public List<Group> getGroups(KeyLocal key, User user) {
        key.validateKey();
        return em.createNamedQuery("findUserGroups").setParameter("user", user).getResultList();
    }



}
